// magekiller3.txt
// A basicnpc that favors mages and priests

// Walker M. White based on an initial design by *i on the Spiderweb forums
// Revision 1.0, 
// March 30, 2004
// Revision history:
// 1.0: Initial release into the wild.

// Permission is granted for free use of this script in scenarios, provided that
// these header comments are retained and modifications are dutifully documented
// throughout.

// This is a second revision of *i's magekiller script.  This script has all
// of the advancements that first magekiller revision has (targetting of
// of NPCS, alternate assessment metric).  It also has one other modification.
// If the current target is not a mage or priest, then the character will
// reassess its target selection in 3 rounds.  

// Why this modification?  A problem with NPC AI, is that when the character
// chooses a target, it does not let go until dead.  As the players are 
// always walking into rooms, generally the front party members are the
// first ones seen and hence the first ones targeted.  Thus, while the
// script may favor spell casters, the NPCs go after the fighters and will
// not switch to the casters until the party fighters are dead (which
// almost never happens).

// Again, the metric for spellcasting strength is 
//
//                (mage/priest level) x (spellcasting bonus)
//
// See magekiller for why this choice of metric.  If you feel that you have a 
// better metric, feel free to change it.  The place to change the metric is 
// marked CHANGE ASSESSMENT METRIC HERE Note that this metric only appropriate 
// for comparing mages to mages and priests to priests.  Mathematically 
// comparing priests to mages requires a greater understanding of the game 
// mechanics than I currently have.

// This script has the standard memory cells of basicnpc.  That is, the memory
// cells used in this file are as follows:
// Cell 0: Mobility          (as per basicnpc)
// Cell 1: Stuff done flag 1 (as per basicnpc)
// Cell 2: Stuff done flag 2 (as per basicnpc)
// Cell 3: Dialog node       (as per basicnpc)

begincreaturescript;

variables;

// Target acquisition variable.
short target;

// Loop variables.
short i, j, class;

// Strength assessment
short strength, max_strength;

// Variables to switch targets if not on first choice.
short first_choice;
short last_acquire;

short start_hp,end_hp,move,trgt,atktyp,odds_steal,steal_amt,xp_lvlup;

body;

beginstate INIT_STATE;
	if (get_memory_cell(0) == 2) {
		set_mobility(ME,0);
	}
	last_acquire = get_current_tick();
break;

beginstate DEAD_STATE;
	// Set the appropriate stuff done flag for this character being dead
	if ((get_memory_cell(1) != 0) || (get_memory_cell(2) != 0)) {
		set_flag(get_memory_cell(1),get_memory_cell(2),1);
	}

	if ((get_memory_cell(5) != 0) || (get_memory_cell(6) != 0))
inc_flag(get_memory_cell(5),get_memory_cell(6),get_memory_cell(7));

break;

beginstate START_STATE;
	// if I have a target for some reason, go attack it
	if (target_ok()) {
		if (dist_to_char(get_target()) <= 16) {
			set_state_continue(3);
		} else { 
			set_target(ME,-1);
		}
	}

	// A quick check for a hostile in range; it is not our actual target.
	// This drops us into the real target acquisition state.
	if (select_target(ME,8,0)) {
		// Initialize no targets as found yet.
		target = -1;
		set_state_continue(4);
	}
	
	// Otherwise, just peacefully move around. Go back to start, if I'm too far
	// from where I started.
	if ((my_dist_from_start() >= 6) || 
		((my_dist_from_start() > 0) && (get_memory_cell(0) > 0))) {
		if (get_ran(1,1,100) < 40) {
			return_to_start(ME,1);
		}
	} else if (get_memory_cell(0) == 0) {
		fidget(ME,25);
	}

	// if we're in combat and the above didn't give me anything to do, just
	// stop now. Otherwise, game will keep the running script.
	if (am_i_doing_action() == FALSE) {
		end_combat_turn();
	}
break;

beginstate 3; // Attacking.  Periodically reassess targets not first choice.
	if (target_ok() == FALSE) {
		set_state(START_STATE);
	} else if (first_choice == FALSE) {
		// Right now, we check every three rounds.  Adjust to your liking.
		if (tick_difference(last_acquire,get_current_tick()) > 2) {
			set_target(ME,-1);
			set_state(START_STATE);
		}
	}
	do_attack();

	end_hp = get_health(trgt);
	if(start_hp > end_hp){
		odds_steal = (get_stat(trgt,1) + get_stat(trgt,9) + get_stat(trgt,18) + get_stat(trgt,20) + get_stat(trgt,23));
		if(odds_steal > (get_stat(my_number(),1) + get_stat(my_number(),9) + get_stat(my_number(),18) + get_stat(my_number(),20)))
			odds_steal = (get_stat(my_number(),1) + get_stat(my_number(),9) + get_stat(my_number(),18) + get_stat(my_number(),20));
		if(get_ran(1,0,(get_stat(my_number(),1) + get_stat(my_number(),9) + get_stat(my_number(),18) + get_stat(my_number(),20))) >= odds_steal){
			steal_amt = (start_hp - end_hp);
			atktyp = get_ran(1,0,11);
			if((atktyp >= 0) && (atktyp <= 4)){
				if(coins_amount() == 0){
					atktyp = (get_ran(1,5,11);
				}
				else{
					print_str_color("Your gold is stolen!",4);
					if(steal_amt > coins_amount())
						steal_amt = (coins_amount);
					change_coins(steal_amt * -1);
				}
			}
			if((atktyp >= 5) && (atktyp <= 8)){
				print_str_color("Your attention is stolen!",4);
				steal_amt = (steal_amt / 5);
				set_char_status(trgt,1,(steal_amt * -1),0,0);
			}
			if((atktyp >= 9) && (atktyp <= 11)){
				print_str_color("Your experience is stolen!",4);
				steal_amt = (steal_amt / 2);
				change_char_xp(trgt,(steal_amt * -1));
				xp_lvlup = (xp_lvlup + steal_amt);
				while(xp_lvlup >= 50){
					xp_lvlup = (xp_lvlup - 50);
set_level(my_number(),(get_level(my_number()) + 1));
				}
			}
		}
		else{
			print_str("Steal fails.");
		}
	}

break;

beginstate 4; // The real target acquisition state.
	// Making a new choice.
	last_acquire = get_current_tick();

	// Have I been hit by nearby living creature? Strike back!
	if (who_hit_me() >= 0) {
		if (dist_to_char(who_hit_me()) <= 2 &&
			char_ok(who_hit_me()) == TRUE) {
				set_target(ME,who_hit_me());
				first_choice = FALSE; // Not neccessarily a mage.
				set_state_continue(3);
		}
	}
	
	// Find mage and/or priest targets.  Randomly pick a class.
	class = get_ran(1,0,1);
	
	// Continue until a target is found or we searched both classes.
	target = -1;
	i = 0;
	while (i < 2 && target == -1) {		
		// Find a target of the current skill class.
		// Break ties with first one found.
		j = 0;
		while (j < 120) {
			// Only waste time on visible, magic using hostiles.
			if (can_see_char(j) == TRUE && 
				char_attitude_to_char(ME,j) == 2 &&
				get_stat(j,11 + class) > 0) {

				// CHANGE ASSESSMENT METRIC HERE
				
				// Get the bonus
				strength = get_stat(j,11 + class) + get_stat(j,2) 
							+ get_stat(j,25);
				
				// Multiply it times the skill.
				strength = strength * get_stat(j,11 + class);
				
				// END ASSESSMENT METRIC
				
				// Check if stronger.
				if (strength > max_strength) {
					max_strength = strength;
					target = j;
				}
			
			}
			j = j + 1;
		}
		
		// Go to the next skill class if nothing found.
		i = i + 1;
		class = 1 - class;
	}
	
	// Found something, attack it.
	if (target != -1) {
		set_target(ME,target);
		first_choice = TRUE;
		set_state_continue(3);
	}	

	// If nothing found, just pick any target.
	if (select_target(ME,8,0)) {
		first_choice = FALSE;
		set_state_continue(3);
	}
	
	// If no one close, we defend ourselves against ranged attackers.
	if (who_hit_me() >= 0) {
		// Target may have been killed since it struck.
		if (char_ok(who_hit_me()) == TRUE) {
			set_target(ME,who_hit_me());
			first_choice = FALSE;
			set_state_continue(3);
		}
	}
	
	// No one is found, so return to start.
	set_state(START_STATE);
break;

beginstate TALKING_STATE;
	if (get_memory_cell(3) == 0) {
		print_str("Talking: It doesn't respond.");
		end();
	}
	begin_talk_mode(get_memory_cell(3));
break;

